package com.xinchuang.utils;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.xinchuang.util.DeoUtil;
import com.xinchuang.util.LngLatUtil;

import java.awt.geom.Point2D;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
 * TODO 栅格计算算法
 *
 * @author zkwczx
 * @version 1.0
 * @date 2024/7/17 10:37
 */
public class GridUtil {

    /**
     * 圆形切圆形
     * @param radius
     * @param isRadius
     * @param positions
     * @return
     */
    public static JSONArray roundToRound(double radius, double isRadius, JSONArray positions) {
        double middleRadius = 0;
        int count = 0;
        JSONArray main = new JSONArray();
        while (true) {
            if (isRadius > radius) {
                main.add(RunExeUtil.getJsons("round", positions.getDouble(0), positions.getDouble(1), isRadius));
                break;
            }
            middleRadius = radius - (isRadius * 2 * count) - isRadius;
            if (middleRadius <= isRadius) {
                BigDecimal first = new BigDecimal(radius - isRadius * 2 * count);
                BigDecimal last = new BigDecimal(isRadius);
                if (first.compareTo(last) == 0) {
                    main.add(RunExeUtil.getJsons("round", positions.getDouble(0), positions.getDouble(1), isRadius));
                }
                break;
            }
            JSONArray middle = RunExeUtil.toRound(positions.getDouble(0), positions.getDouble(1), middleRadius, isRadius);
            main.addAll(middle);
            count++;
        }
        return main;
    }

    /**
     * 矩形切圆形
     * @param radius
     * @param position
     * @return
     */
    public static JSONArray rectangleToRound(double radius, JSONArray position) {
        JSONArray main = new JSONArray();
        //获取坐标
        Double minLng = 0.0;
        Double maxLng = 0.0;
        Double minLat = 0.0;
        Double maxLat = 0.0;
        minLng = position.getJSONArray(0).getDouble(0);
        if (minLng > position.getJSONArray(1).getDouble(0)) {
            maxLng = minLng;
            minLng = position.getJSONArray(1).getDouble(0);
        } else {
            maxLng = position.getJSONArray(1).getDouble(0);
        }
        minLat = position.getJSONArray(0).getDouble(1);
        if (minLat > position.getJSONArray(1).getDouble(1)) {
            maxLat = minLat;
            minLat = position.getJSONArray(1).getDouble(1);
        } else {
            maxLat = position.getJSONArray(1).getDouble(1);
        }
        //获取两个中心点的距离(米)
        double length = LngLatUtil.getDistance(minLng, maxLat, maxLng, maxLat) * 1000;
        //获取矩形对边的长度
        double width = LngLatUtil.getDistance(minLng, maxLat, minLng, minLat) * 1000;
        int wSign = (int) (width / (radius * 2));
        int lSign = (int) (length / (radius * 2));
        //输入的半径大于矩形的中线长度
        if (wSign <= 1 || radius * 2 >= length) {
            //计算两边的中心点坐标
            Map<String, Double> point1 = null;
            {
                LinkedList<LngLatUtil.Position> postionList = new LinkedList<>();
                postionList.add(new LngLatUtil.Position(maxLat, minLng));
                postionList.add(new LngLatUtil.Position(minLat, minLng));
                point1 = LngLatUtil.getCenterPoint(postionList);
            }
            Map<String, Double> point2 = null;
            {
                LinkedList<LngLatUtil.Position> postionList = new LinkedList<>();
                postionList.add(new LngLatUtil.Position(maxLat, maxLng));
                postionList.add(new LngLatUtil.Position(minLat, maxLng));
                point2 = LngLatUtil.getCenterPoint(postionList);
            }
            LinkedList<LngLatUtil.Position> postionList = new LinkedList<>();
            postionList.add(new LngLatUtil.Position(point1.get("centerLat"), point1.get("centerLon")));
            postionList.add(new LngLatUtil.Position(point2.get("centerLat"), point2.get("centerLon")));
            Map<String, Double> centerPoint = LngLatUtil.getCenterPoint(postionList);
            main.add(RunExeUtil.getJsons("round", centerPoint.get("centerLon"), centerPoint.get("centerLat"), radius));
        } else {
            //获取两个中心点的方位角
            double lengthAngle = LngLatUtil.bearing(maxLat, minLng, maxLat, maxLng);
            double widthAngle = LngLatUtil.bearing(maxLat, minLng, minLat, minLng);
            for (int i = 0; i < wSign; i++) {
                List<Double> widthLngLat = DeoUtil.computerThatLonLat(minLng, maxLat, widthAngle, radius + (radius * 2 * i));
                for (int j = 0; j < lSign; j++) {
                    List<Double> lonLat = DeoUtil.computerThatLonLat(widthLngLat.get(0), widthLngLat.get(1), lengthAngle, radius + (radius * 2 * j));
                    main.add(RunExeUtil.getJsons("round", lonLat.get(0), lonLat.get(1), radius));
                }
            }
        }
        return main;
    }

    public static JSONArray rectangleToRectangle(double width, JSONArray position) {
        JSONArray main = new JSONArray();
        //获取坐标
        Double minLng = 0.0;
        Double maxLng = 0.0;
        Double minLat = 0.0;
        Double maxLat = 0.0;
        minLng = position.getJSONArray(0).getDouble(0);
        if (minLng > position.getJSONArray(1).getDouble(0)) {
            maxLng = minLng;
            minLng = position.getJSONArray(1).getDouble(0);
        } else {
            maxLng = position.getJSONArray(1).getDouble(0);
        }
        minLat = position.getJSONArray(0).getDouble(1);
        if (minLat > position.getJSONArray(1).getDouble(1)) {
            maxLat = minLat;
            minLat = position.getJSONArray(1).getDouble(1);
        } else {
            maxLat = position.getJSONArray(1).getDouble(1);
        }
        //获取两个中心点的距离(米)
        double length = LngLatUtil.getDistance(minLng, maxLat, maxLng, maxLat) * 1000;
        //获取两个中心点的方位角
        double lengthAngle = LngLatUtil.bearing(maxLat, minLng, maxLat, maxLng);
        if (width >= length) {
            List<Double> lonLat = DeoUtil.computerThatLonLat(minLng, minLat, lengthAngle, width);
            main.add(RunExeUtil.getJsons("rectangle", toRectangleJson(minLng, maxLat, lonLat.get(0), lonLat.get(1))));
        } else {
            int sign = (int) (length / width);
            for (int i = 0; i < sign; i++) {
                if (i > 0) {
                    List<Double> firstLngLat = DeoUtil.computerThatLonLat(minLng, maxLat, lengthAngle, width);
                    minLng = firstLngLat.get(0);
                    maxLat = firstLngLat.get(1);
                }
                //移动的距离
                List<Double> lastLngLat = DeoUtil.computerThatLonLat(minLng, minLat, lengthAngle, width);
                double middleLng = lastLngLat.get(0);
                double middleLat = lastLngLat.get(1);
                if (minLng > middleLng) {
                    double lng = middleLng;
                    middleLat = minLat;
                    minLat = lng;
                }
                if (maxLat < middleLat) {
                    double lat = middleLat;
                    middleLng = maxLat;
                    maxLat = lat;
                }
                main.add(RunExeUtil.getJsons("rectangle", toRectangleJson(minLng, maxLat, middleLng, middleLat)));
            }
        }
        return main;
    }

    private static JSONArray toRectangleJson(double minLng, double minLat, double maxLng, double maxLat) {
        JSONArray position = new JSONArray();
        position.add(Arrays.asList(minLng, maxLat, 0));
        position.add(Arrays.asList(maxLng, minLat, 0));
        return position;
    }

    public static JSONArray geoPolygonToGeoPolygon(double width, JSONArray position) {
        JSONArray main = new JSONArray();
        if (position.size() != 4) {
            return main;
        }
        JSONArray point1 = position.getJSONArray(0);
        JSONArray point2 = position.getJSONArray(1);
        JSONArray point3 = position.getJSONArray(2);
        JSONArray point4 = position.getJSONArray(3);

        {
            double v1 = LngLatUtil.getDistance(point1.getDouble(0), point1.getDouble(1), point2.getDouble(0), point2.getDouble(1));
            double v2 = LngLatUtil.getDistance(point2.getDouble(0), point2.getDouble(1), point3.getDouble(0), point3.getDouble(1));
            if (v1 > v2) {
                JSONArray middleArray = point1;
                point1 = point2;
                point2 = point3;
                point3 = point4;
                point4 = middleArray;
            }
        }

        double bearingAngle1 = LngLatUtil.bearing(point2.getDouble(1), point2.getDouble(0), point3.getDouble(1), point3.getDouble(0));
        double bearingAngle2 = LngLatUtil.bearing(point1.getDouble(1), point1.getDouble(0), point4.getDouble(1), point4.getDouble(0));

        System.out.println("A 的方位角：" + bearingAngle1);
        System.out.println("B 的方位角：" + bearingAngle2);

        double distance1 = LngLatUtil.getDistance(point2.getDouble(0), point2.getDouble(1), point3.getDouble(0), point3.getDouble(1)) * 1000;
        double distance2 = LngLatUtil.getDistance(point1.getDouble(0), point1.getDouble(1), point4.getDouble(0), point4.getDouble(1)) * 1000;

        double w = distance1;
        if (distance1 > distance2) {
            w = distance2;
        }

        if (width >= w) {
            main.add(RunExeUtil.getJsons("GeoPolygon", toGeoClassificationPolygonJson(point1, point2, point3, point4)));
        } else {
            int sign = (int) (w / width);
            List<Double> middlePoint1 = new LinkedList<>();
            middlePoint1.add(point2.getDouble(0));
            middlePoint1.add(point2.getDouble(1));
            List<Double> middlePoint2 = new LinkedList<>();
            middlePoint2.add(point1.getDouble(0));
            middlePoint2.add(point1.getDouble(1));
            for (int i = 0; i < sign; i++) {
                List<Double> lonLat1 = DeoUtil.computerThatLonLat(middlePoint1.get(0), middlePoint1.get(1), bearingAngle1, width);
                List<Double> lonLat2 = DeoUtil.computerThatLonLat(middlePoint2.get(0), middlePoint2.get(1), bearingAngle2, width);
                main.add(RunExeUtil.getJsons("GeoPolygon", toGeoClassificationPolygonJson(middlePoint2, middlePoint1, lonLat1, lonLat2)));
                middlePoint1 = lonLat1;
                middlePoint2 = lonLat2;
            }
        }

        return main;
    }

    private static JSONArray toGeoClassificationPolygonJson(Object position1, Object position2, Object position3, Object position4) {
        JSONArray position = new JSONArray();
        position.add(position1);
        position.add(position2);
        position.add(position3);
        position.add(position4);
        return position;
    }


//    public static void main(String[] args) {
//        double Ax = 86.98791385737681;
//        double Ay = 24.422904172012814;
//        double Bx = 87.86368618923824;
//        double By = 23.250488464199613;
//        double Ex = 89.7345451879565;
//        double Ey = 24.506951606538138;
//
//        // Calculate the slope of AB
//        double slopeAB = (By - Ay) / (Bx - Ax);
//
//        // Calculate the intercept for the line through E parallel to AB
//        double interceptE = Ey - slopeAB * Ex;
//
//        // Calculate the perpendicular slopes and intercepts
//        double perpSlope = -1 / slopeAB;
//        double interceptB = By - perpSlope * Bx;
//        double interceptA = Ay - perpSlope * Ax;
//
//        // Solve for C and D
//        double Cx = (interceptB - interceptE) / (slopeAB - perpSlope);
//        double Cy = slopeAB * Cx + interceptE;
//        double Dx = (interceptA - interceptE) / (slopeAB - perpSlope);
//        double Dy = slopeAB * Dx + interceptE;
//
//        System.out.println("C: (" + Cx + ", " + Cy + ")");
//        System.out.println("D: (" + Dx + ", " + Dy + ")");

//        Point2D.Double a = new Point2D.Double(86.98791385737681, 24.422904172012814);
//        Point2D.Double b = new Point2D.Double(87.86368618923824, 23.250488464199613);
//        Point2D.Double e = new Point2D.Double(89.7345451879565, 24.506951606538138);
//
//        // 计算向量AB
//        Point2D.Double ab = new Point2D.Double(b.getX() - a.getX(), b.getY() - a.getY());
//        double abMagnitude = ab.distance(0, 0);
//
//        // 计算向量BE
//        Point2D.Double be = new Point2D.Double(e.getX() - b.getX(), e.getY() - b.getY());
//        double beMagnitude = be.distance(0, 0);
//
//        // 调整向量BE的长度等于AB的长度
//        Point2D.Double beAdjusted = new Point2D.Double(
//                (be.getX() / beMagnitude) * abMagnitude,
//                (be.getY() / beMagnitude) * abMagnitude
//        );
//
//        // 应用调整后的向量BE到点A得到D点
//        Point2D.Double d = new Point2D.Double(
//                a.getX() + beAdjusted.getX(),
//                a.getY() + beAdjusted.getY()
//        );
//
//        // 应用调整后的向量BE到点B得到C点
//        Point2D.Double c = new Point2D.Double(
//                b.getX() + beAdjusted.getX(),
//                b.getY() + beAdjusted.getY()
//        );
//
//        System.out.println("C: " + c);
//        System.out.println("D: " + d);
//    }

    public static void main(String[] args) {
        BigDecimal Ax = new BigDecimal("86.98791385737681");
        BigDecimal Ay = new BigDecimal("24.422904172012814");
        BigDecimal Bx = new BigDecimal("87.86368618923824");
        BigDecimal By = new BigDecimal("23.250488464199613");
        BigDecimal Ex = new BigDecimal("89.7345451879565");
        BigDecimal Ey = new BigDecimal("24.506951606538138");

        BigDecimal slopeAB = divide((By.subtract(Ay)), (Bx.subtract(Ax)));
        BigDecimal interceptE = divide((Ey.subtract(slopeAB.multiply(Ex))), new BigDecimal("1"));
        BigDecimal perpSlope = divide(new BigDecimal("-1"), slopeAB);
        BigDecimal interceptB = divide((By.subtract(perpSlope.multiply(Bx))), new BigDecimal("1"));
        BigDecimal interceptA = divide((Ay.subtract(perpSlope.multiply(Ax))), new BigDecimal("1"));

        BigDecimal Cx = divide((interceptB.subtract(interceptE)), (slopeAB.subtract(perpSlope)));
        BigDecimal Cy = slopeAB.multiply(Cx).add(interceptE);
        BigDecimal Dx = divide((interceptA.subtract(interceptE)), (slopeAB.subtract(perpSlope)));
        BigDecimal Dy = slopeAB.multiply(Dx).add(interceptE);

        System.out.println("C: (" + Cx + ", " + Cy + ")");
        System.out.println("D: (" + Dx + ", " + Dy + ")");
    }

    private static BigDecimal divide(BigDecimal numerator, BigDecimal denominator) {
        return numerator.divide(denominator, 15, RoundingMode.HALF_UP);
    }

}
